#include <string.h>
#include "gkt_board.h"
#include "gkt_debug.h"
#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE
#include "gkt_screen.h"
#endif
#include "gkt_wdt.h"
#include "gkt_os.h"
#include "gkt_malloc.h"
#include "gkt_window.h"
#include "gkt_wdt_ext.h"

#define WINDOW_CURRENT_DEPTH_MAX	2

struct _window_manager {
	uint32_t	window_descs_num;
	const gkt_window_desc_s *window_descs;

	gkt_window_s	*windows[GKT_WINDOW_ID_USER_BASE];
	gkt_window_s	*window_show_pending;	/* the window to be showed */

	gkt_list_s	windows_head;
	gkt_list_s	windows_destroy_pending_head;	/* destroy pending list */

	int	window_switch_enable;
	gkt_window_s	*disable_switch_window;

	int	window_current_depth;
	gkt_window_s	*window_current_table[WINDOW_CURRENT_DEPTH_MAX];
};
static struct _window_manager s_window_manager;

int gkt_window_active_is_start(void)
{
	return (s_window_manager.windows[GKT_WINDOW_ID_ACTIVE] == 
		s_window_manager.windows[GKT_WINDOW_ID_START]) ? 1 : 0;
}

gkt_window_s *gkt_window_find(gkt_window_id_t id)
{
	gkt_window_s *window = NULL;
	gkt_window_s *window_tmp;
	gkt_list_s *pos;

	if (id >= GKT_WINDOW_ID_USER_BASE) {
		gkt_list_foreach(&s_window_manager.windows_head, pos) {
			window_tmp = __CONTAINER_OF(pos, gkt_window_s, link);
			if (window_tmp->desc->id == id) {
				window = window_tmp;
				break;
			}
		}
	}
	else if (id >= GKT_WINDOW_ID_START)
		window = s_window_manager.windows[id];
	else {	/* id < 0 */			
		window_tmp = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
		if (!window_tmp)
			window_tmp = s_window_manager.windows[GKT_WINDOW_ID_ACTIVE];

		if (window_tmp 
			&& (window_tmp->flags & GKT_WINDOW_F_LINKED)) 
		{
			do {
				pos = window_tmp->link.prev;
				if (pos == &s_window_manager.windows_head)
					break;

				id++;
				window_tmp = __CONTAINER_OF(pos, gkt_window_s, link);
				if (0 == id){
					window = window_tmp;
					break;
				}
			} while(1);
		}
	}

	return window;
}

int gkt_window_custom_event_handle(gkt_window_s *window,
				uint32_t major_type, uint32_t minor_type, 
				uint32_t id, uint32_t data)
{
	int retval;

	GKT_ASSERT(window);

	if (__LIKELY(window->desc->event_handler)) {
		gkt_trace("window_event: [C] (%d) (%u - %u - %u - %u)\n",
					window->desc->id, major_type, minor_type, id, data);

		GKT_ASSERT(s_window_manager.window_current_depth < WINDOW_CURRENT_DEPTH_MAX);

		s_window_manager.windows[GKT_WINDOW_ID_CURRENT] = window;

		s_window_manager.window_current_table[s_window_manager.window_current_depth++] = window;
		retval = (*window->desc->event_handler)(major_type, minor_type, id, data);
		s_window_manager.window_current_depth--;
	
		if (!s_window_manager.window_current_depth) 
			s_window_manager.windows[GKT_WINDOW_ID_CURRENT] = NULL;
		else {
			s_window_manager.windows[GKT_WINDOW_ID_CURRENT] = 
				s_window_manager.window_current_table[s_window_manager.window_current_depth - 1];
		}
	}
	else
		retval = GKT_EVENT_HANDLE_CONTINUE;

	return retval;
}

#ifdef GKT_CONFIG_WINDOW_WIDGET_MAXNUMS
#define WINDOW_WIDGET_MAXNUMS	GKT_CONFIG_WINDOW_WIDGET_MAXNUMS
#else
#define WINDOW_WIDGET_MAXNUMS	32
#endif
static gkt_window_s *window_create(gkt_window_id_t id, uint32_t data)
{
	const gkt_window_desc_s *window_desc;
	gkt_window_s *window = NULL;
	gkt_widget_s *widget;
	uint32_t size, i;
	int retval;

	gkt_trace("window_create: id(%u), data(0x%x)\n", id, data);

	window_desc = NULL;
	for (i = 0; i < s_window_manager.window_descs_num; i++) {
		if (s_window_manager.window_descs[i].id == id) {
			window_desc = &s_window_manager.window_descs[i];
			break;
		}
	}

	if (__LIKELY(window_desc)) {
		GKT_ASSERT(window_desc->widget_nums <= WINDOW_WIDGET_MAXNUMS);
		size = sizeof(gkt_window_s) + (sizeof(gkt_widget_s) * window_desc->widget_nums);
		window = (gkt_window_s *)gkt_malloc(size);
		if (__LIKELY(window)) {
			memset((void *)window, 0, size);

			window->flags |= GKT_WINDOW_F_ACTIVE_1ST;
			window->desc = window_desc;
			window->widgets = (gkt_widget_s *)(window + 1);

			/* widget init */
			widget = window->widgets;
			for (i = 0; i < (uint32_t)window_desc->widget_nums; i++) {
				widget->flags = (uint32_t)window_desc->widget_descs[i].flags & GKT_WIDGET_F_DESC_MASK;
				widget->flags |= GKT_WIDGET_F_SHOWABLE;	/* default */
				widget->id = (gkt_widget_id_t)i;
				widget->desc = window_desc->widget_descs + i;
				widget->window = window;
#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE \
		&& GKT_WIDGET_MULTIPLE_COLOR_ENABLE
				widget->fg_color = window_desc->widget_descs[i].fg_color;
#if GKT_WIDGET_BGCOLOR_VARIOUS
				widget->bg_color = window_desc->widget_descs[i].bg_color;
#endif
#endif
				gkt_widget_event_dispatch(widget, GKT_EVENT_WIDGET, 
						GKT_WIDGET_EVENT_INIT, 0);

				widget++;
			}

			/* window custom init */
			retval = gkt_window_custom_event_handle(window, 
							GKT_EVENT_WINDOW, GKT_WINDOW_INIT, 
							0, data);
			if (__UNLIKELY(retval < 0)) {
				gkt_error("window_create: window custom init failed %d\n", retval);

				widget = window->widgets;
				for (i = 0; i < (uint32_t)window_desc->widget_nums; i++) {
					gkt_widget_event_dispatch(widget, 
							GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_UNINIT, 
							0);
					widget++;
				}

				gkt_free(window);
				return NULL;
			}
		}
	}
	else
		gkt_error("window_create: window(%d) does not exist!\n", id);

	return window;
}

static void window_hide(gkt_window_s *window)
{
	uint32_t i;

	gkt_trace("window_hide: id(%d), flags(0x%x)\n", 
				window->desc->id, window->flags);

	/* clear window showed flag */
	if (window->flags & GKT_WINDOW_F_SHOW) {
		window->flags &= ~GKT_WINDOW_F_SHOW;

		/* clear SHOW attribute of all widgets belong to this window */
		for (i = 0; i < (uint32_t)window->desc->widget_nums; i++)
			gkt_widget_hide(&window->widgets[i]);
	}

	if (s_window_manager.windows[GKT_WINDOW_ID_ACTIVE] == window) {
		/* let custom window event handler to response to INACTIVE if needed */
		gkt_window_custom_event_handle(window, 
				GKT_EVENT_WINDOW, GKT_WINDOW_INACTIVE, 
				0, 0);
		window->flags &= ~GKT_WINDOW_F_ACTIVE_1ST;

		s_window_manager.windows[GKT_WINDOW_ID_ACTIVE] = NULL;

		gkt_window_timer_clear_all(window->desc->id);
		gkt_window_voice_stop(0);
	}
}

static void window_destroy(gkt_window_s *window)
{
	gkt_window_s *window_current;
	gkt_widget_s *widget;
	uint32_t i;

	gkt_trace("window_destroy: id(%d), flags(0x%x)\n", 
				window->desc->id, window->flags);

	window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
	if (window_current) {
		gkt_trace("window_destroy: current id(%d), flags(0x%x)\n", 
					window_current->desc->id, window_current->flags);

		/* window destroy pending */
		gkt_list_delete(&window->link);
		window->flags &= ~GKT_WINDOW_F_LINKED;

		gkt_list_add_tail(&s_window_manager.windows_destroy_pending_head, &window->link);
	} 
	else {
		window_hide(window);

		/* notify the custom window to unint */
		gkt_window_custom_event_handle(window, 
				GKT_EVENT_WINDOW, GKT_WINDOW_UNINIT, 
				0, 0);

		/* uninit all widgets of this window */
		widget = window->widgets;
		for (i = 0; i < window->desc->widget_nums; i++) {
			gkt_widget_event_dispatch(widget, 
					GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_UNINIT, 
					0);
			widget++;
		}

		gkt_list_delete(&window->link);
		gkt_free(window);
	}
}

static void window_show(gkt_window_s *window)
{
	gkt_window_s *window_active;
	gkt_window_s *window_current;
	gkt_widget_s *widget;
	uint32_t active_1st, i;

	gkt_trace("window_show: id(%d), flags(0x%x)\n", 
				window->desc->id, window->flags);

	window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
	if (window_current) {
		gkt_trace("window_show: current id(%d), flags(0x%x)\n", 
					window_current->desc->id, window_current->flags);
		/* in the context of custom window handler */
		s_window_manager.window_show_pending = window;
	}
	else {
		window_active = s_window_manager.windows[GKT_WINDOW_ID_ACTIVE];
		if (window_active) {
			if (window_active != window)
				window_hide(s_window_manager.windows[GKT_WINDOW_ID_ACTIVE]);
			else {
				gkt_trace("window_show: active window!\n");
				return;
			}
		}

#if defined(GKT_CONFIG_KEYPAD_ENABLE) && GKT_CONFIG_KEYPAD_ENABLE
		gkt_key_reset();
#endif
#if GKT_CONFIG_VOICE_OUTPUT_ENABLE
		window->voice_id = 0;
		window->voice_request_id = -1;
#endif

		/* set window as current/active/showed window */
		s_window_manager.windows[GKT_WINDOW_ID_ACTIVE] = window;
		/* do some updating works in window event handler before window is showed */
		active_1st = (window->flags & GKT_WINDOW_F_ACTIVE_1ST) ? 1 : 0;
		gkt_window_custom_event_handle(window, 
				GKT_EVENT_WINDOW, GKT_WINDOW_ACTIVE, 
				0, active_1st);

		if (!s_window_manager.window_show_pending) {
#if GKT_CONFIG_VOICE_OUTPUT_ENABLE
			if (window->voice_id > 0) {
				gkt_window_voice_play(&window->voice_request_id, 
						window->voice_id, window->voice_flags, NULL);
				gkt_trace("window_show: voice_request_id = %d\n", 
					window->voice_request_id);
			}
#endif

#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE
			gkt_screen_control(GKT_SCREEN_CTRL_DISPLAY_CLEAR, 0);
#endif

			if (window->desc->widget_nums) {
				/* show all showable widgets */
				for (i = 0; i < (uint32_t)window->desc->widget_nums; i++) {
					widget = &window->widgets[i];
					if (!(widget->flags & GKT_WIDGET_F_SHOWABLE))
						continue;

					/* decide focus widget */
					if (!window->widget_focus) {
						if (widget->flags & GKT_WIDGET_F_FOCUSABLE) {
							widget->flags |= GKT_WIDGET_F_FOCUS;
							window->widget_focus = widget;
						}
					}
					else if (window->widget_focus != widget)
						widget->flags &= ~GKT_WIDGET_F_FOCUS;

					gkt_widget_show(widget, 0);
				}
			}

			/* window is showed */
			window->flags |= GKT_WINDOW_F_SHOW;

#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE
			gkt_screen_update(NULL);
#endif
		}
	}
}

static void window_pending_process(void)
{
	gkt_window_s *window;
	gkt_list_s *pos;

	if (gkt_list_is_not_empty(s_window_manager.windows_destroy_pending_head)
			|| s_window_manager.window_show_pending)
	{
		gkt_window_timeout_disable("window_pending - start");

		do {
			gkt_list_foreach(&s_window_manager.windows_destroy_pending_head, pos) {
				window = __CONTAINER_OF(pos, gkt_window_s, link);
				window_destroy(window);
			}

			if (s_window_manager.window_show_pending) {
				window = s_window_manager.window_show_pending;
				s_window_manager.window_show_pending = NULL;
				window_show(window);
			}
		} while (gkt_list_is_not_empty(s_window_manager.windows_destroy_pending_head)
					|| s_window_manager.window_show_pending);

		gkt_window_timeout_enable("window_pending - end");
	} 
}

int gkt_window_show(gkt_window_id_t id, uint32_t data)
{
	gkt_window_s *window, *window_current, *window_tmp;
	int retval = GKT_SUCCESS;

	gkt_trace("gkt_window_show: id(%d), data(0x%x)\n", id, data);

	if (!s_window_manager.window_switch_enable) {
		window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
		if (window_current != s_window_manager.disable_switch_window) {
			gkt_printf("gkt_window_show: (%d) window switch is disabled!\n", id);
			return GKT_EPERMIT;
		}
		else {
			s_window_manager.disable_switch_window = NULL;
			s_window_manager.window_switch_enable = 1;
		}
	}

	gkt_window_timeout_disable("window_show - start");

	window = gkt_window_find(id);
	if (!window) {
		/* new window
		 * - window does not exist in window history list
		 * - FORWARD 
		 * 
		 * - Example
		 *    Window History List: Win-A -> Win-B -> Win-C
		 *    Active Window: Win-C
		 *    Current Window: Win-C
		 *    Target Window To Show: Win-D
		 *   So,
		 *    1. Win-C will be hide (not destroyed)
		 *    2. new window - Win-D will be created & showed
		 *    3. Then, the Active Window is chaned to Win-D
		 *    4. Final Window History List: Win-A -> Win-B -> Wind-C -> Win-D
		 *
		 * - Example
		 *    Window History List: Win-A -> Win-B -> Win-C
		 *    Active Window: Win-C
		 *    Current Window: Win-B
		 *    Target Window To Show: Win-D
		 *   SO,
		 *    1. Win-C will be hide & destroyed
		 *    2. new window - Win-D will be created & showed
		 *    3. Then, the Active Window is chaned to Win-D
		 *    4. Final Window History List: Win-A -> Win-B -> Win-D
		 *
		 * - Example
		 *    Window History List: Win-A -> Win-B -> WinC
		 *    Active Window: Win-C
		 *    Current Window: NULL
		 *    Target Window To Show: Win-D
		 *   So,
		 *    1. Win-C will be hide (not destroyed)
		 *    2. new window - Win-D will be created & showed
		 *    3. Then, the Active Window is chaned to Win-D
		 *    4. Final Window History List: Win-A -> Win-B -> Wind-C -> Win-D
		 *
		 * - Example
		 *    Window History List: $$$${ empty }$$$$
		 *    Active Window: NULL
		 *    Current Window: NULL
		 *    Target Window To Show: Win-A
		 *  So,
		 *    1. new window - Win-A will be created & showed
		 *    2. Then, the Active Window is chaned to Win-A
		 *    3. Final Window History List: Win-A
		 */
		window = window_create(id, data);
		if (__UNLIKELY(!window)) {
			retval = GKT_ENOTEXIST;
			goto done;
		}

		window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
		if (window_current 
			&& (window_current->flags & GKT_WINDOW_F_LINKED)) 
		{
			do {
				window_tmp = __CONTAINER_OF(s_window_manager.windows_head.prev, 
									gkt_window_s, link);
				if (window_tmp == window_current)
					break;
				window_destroy(window_tmp);
			} while(1);
		}

		/* add new window to window list */
		gkt_list_add_tail(&s_window_manager.windows_head, &window->link);
		window->flags |= GKT_WINDOW_F_LINKED;
	}
	else {
		/* exist window
		 * - window exists in list
		 * - BACKWARD 
		 *
		 * - Example
		 *    Window History List: Win-A -> Win-B -> Win-C -> Win-D
		 *    Active Window: Win-D
		 *    Current Window: Win-D
		 *    Target Window To Show: Win-B
		 *   SO, 
		 *    1. Win-D & Win-C will be hide & destroyed successively
		 *    2. Win-B will be showed
		 *    3. Then, the Active Window is chaned to Win-B
		 *    4. Final Window History List: Win-A -> Win-B
		 *
		 * - Example
		 *    Window History List: Win-A -> Win-B -> WinC -> Win-D
		 *    Active Window: Win-D
		 *    Current Window: Win-C
		 *    Target Window To Show: Win-B
		 *   SO, 
		 *    1. Win-D & Win-Cwill be hide & destroyed successively
		 *    2. Win-B will be showed
		 *    3. Then, the Active Window is chaned to Win-B
		 *    4. Final Window History List: Win-A -> Win-B
		 *
		 * - Example
		 *    Window History List: Win-A -> Win-B -> WinC -> Win-D
		 *    Active Window: Win-D
		 *    Current Window: NULL
		 *    Target Window To Show: Win-B
		 *   SO, 
		 *    1. Win-D & Win-C will be hide & destroyed successively
		 *    2. Win-B will be showed
		 *    3. Then, the Active Window is chaned to Win-B
		 *    4. Final Window History List: Win-A -> Win-B
		 */
		do {
			window_tmp = __CONTAINER_OF(s_window_manager.windows_head.prev, 
								gkt_window_s, link);
			if (window_tmp == window)
				break;
			window_destroy(window_tmp);
		} while(1);
	}

	/* show window */
	window_show(window);

done:
	gkt_window_timeout_enable("window_show - end");
	return retval;
}

int gkt_window_is_showed(gkt_window_id_t id)
{
	gkt_window_s *window;

	window = gkt_window_find(id);
	return (window && (window->flags & GKT_WINDOW_F_SHOW)) ? 1 : 0;
}

int gkt_window_current_is_showed(void)
{
	gkt_window_s *window;

	window = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
	return (window && (window->flags & GKT_WINDOW_F_SHOW)) ? 1 : 0;
}

void gkt_window_exit(void)
{
	gkt_window_s *window_current, *window_to_show;

	/*
	 * 1. this function should be called from custom window event handler
	 * 2. could not exit from start/home window
	 */
	window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
	if (window_current) {
		if (!s_window_manager.window_switch_enable 
			&& (window_current == s_window_manager.disable_switch_window))
		{
			s_window_manager.disable_switch_window = NULL;
			s_window_manager.window_switch_enable = 1;
		}

		if (window_current == s_window_manager.windows[GKT_WINDOW_ID_START])
			return;
		else if ((window_current == s_window_manager.windows[GKT_WINDOW_ID_ACTIVE])
			&& !s_window_manager.window_show_pending) 
		{
			window_to_show = __CONTAINER_OF(window_current->link.prev, gkt_window_s, link);
		}
		else
			window_to_show = NULL;

		gkt_window_timeout_disable("window_exit - start");

		/* destroy current window */
		window_destroy(window_current);

		/* show window */
		if (window_to_show)
			window_show(window_to_show);

		gkt_window_timeout_enable("window_exit - end");
	}
}

void gkt_window_switch_control(int enable)
{
	gkt_window_s *window_current, *window_active;

	if (s_window_manager.window_switch_enable != enable) {
		window_current = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
		window_active = s_window_manager.windows[GKT_WINDOW_ID_ACTIVE];
		if (window_active && (window_active == window_current)) {
			s_window_manager.window_switch_enable = enable;
			if (!enable)
				s_window_manager.disable_switch_window = window_active;
				
		}
	}
}

#if GKT_CONFIG_VOICE_OUTPUT_ENABLE
void gkt_window_set_voice(uint32_t id, uint32_t flags)
{
	gkt_window_s *window;

	window = s_window_manager.windows[GKT_WINDOW_ID_CURRENT];
	if (window) {
		window->voice_id = id;
		window->voice_flags = flags;
	}
}
#endif

void gkt_window_put_private_data(uint32_t private_data)
{
	GKT_ASSERT(s_window_manager.windows[GKT_WINDOW_ID_CURRENT]);
	s_window_manager.windows[GKT_WINDOW_ID_CURRENT]->private_data = private_data;
}

uint32_t gkt_window_get_private_data(void)
{
	GKT_ASSERT(s_window_manager.windows[GKT_WINDOW_ID_CURRENT]);
	return s_window_manager.windows[GKT_WINDOW_ID_CURRENT]->private_data;
}

#define WINDOW_ACTIVE_RESET_EVENT_TYPE_MASK	\
	(gkt_bit(GKT_EVENT_VOICE) | gkt_bit(GKT_EVENT_KEY) \
		| gkt_bit(GKT_EVENT_FPS) | gkt_bit(GKT_EVENT_CARD) \
		| gkt_bit(GKT_EVENT_BLE) | gkt_bit(GKT_EVENT_USER))

void gkt_window_manager_start(gkt_window_id_t start_window_id)
{
	gkt_window_s *window;
	gkt_event_s event;
	gkt_event_handler event_handler;
	int retval;

	gkt_trace("window_manager: start ...\n");

	retval = gkt_wdte_init();
	gkt_wdte_mark(GKT_WDTE_ID(WINDOW_MANAGER), GKT_WDTE_ST_BUSY);

	retval = gkt_window_show(start_window_id, 0);
	if (retval != GKT_SUCCESS) {
		gkt_error("window_manager: show start window(%d) failed %d\n", 
						start_window_id, retval);
		return;
	}
	s_window_manager.windows[GKT_WINDOW_ID_START] = 
			gkt_window_find(start_window_id);

	gkt_window_timeout_enable("manager_start");

	do {
		window_pending_process();
		gkt_window_system_pending_process();

		gkt_wdte_mark(GKT_WDTE_ID(WINDOW_MANAGER), GKT_WDTE_ST_IDLE);
		retval = gkt_event_get(&event, GKT_INFINITE);
		gkt_wdte_mark(GKT_WDTE_ID(WINDOW_MANAGER), GKT_WDTE_ST_BUSY);
		
		if ((GKT_SUCCESS == retval) 
			&& (event.major_type < GKT_EVENT_MAX)) 
		{
			gkt_trace("window_manager: type(%u - %u)\n",
					event.major_type, event.minor_type);
			if (gkt_bit(event.major_type) & WINDOW_ACTIVE_RESET_EVENT_TYPE_MASK)
				gkt_window_timeout_reset();

			event_handler = gkt_event_get_handler(event.major_type);
			if (event_handler) {
				retval = (*event_handler)(&event);
				if (retval != GKT_EVENT_HANDLE_CONTINUE)
					continue;
			}

			if (event.window_id >= GKT_WINDOW_ID_START) {
				window = gkt_window_find((gkt_window_id_t)event.window_id);
				if (!window) {
					gkt_info("window_manager: window(%d) not find!\n", event.window_id);
					continue;
				}
			}
			else if (__LIKELY(s_window_manager.windows[GKT_WINDOW_ID_ACTIVE]))
				window = s_window_manager.windows[GKT_WINDOW_ID_ACTIVE];
			else {
				gkt_info("window_manager: no active window!\n");
				continue;
			}

			gkt_window_custom_event_handle(window, 
					event.major_type, event.minor_type, 
					0, (uint32_t)event.data);
		}
	} while (1);
}

extern uint32_t __window_descs_start;
extern uint32_t __window_descs_end;
int gkt_window_manager_init(void)
{
	const gkt_window_desc_s *window_descs_start;
	const gkt_window_desc_s *window_descs_end;

	window_descs_start = (const gkt_window_desc_s *)&__window_descs_start;
	window_descs_end = (const gkt_window_desc_s *)&__window_descs_end;	
	s_window_manager.window_descs_num = window_descs_end - window_descs_start;
	s_window_manager.window_descs = window_descs_start;
	GKT_ASSERT(s_window_manager.window_descs_num);
	gkt_trace("window_manager: window_nums = %u\n", s_window_manager.window_descs_num);

	gkt_list_init(&s_window_manager.windows_head);
	gkt_list_init(&s_window_manager.windows_destroy_pending_head);

	s_window_manager.window_switch_enable = 1;
	s_window_manager.disable_switch_window = NULL;

	return gkt_event_init();
}

